﻿using SQLinq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace SimpleDatabaseAdapter
{
    public class AllData : IAllData
    {
        private SimpleDataAdapter _Parent;
        internal AllData(SimpleDataAdapter parent)
        {
            _Parent = parent;
        }

        public void CheckTable()
        {
            _Parent.TableCreator.CheckAllDataTable();
        }
        public DataTable GetData(string query)
        {
            DataTable dt = _Parent.DatabaseAccess.GetData(query);
            return dt;
        }
        public DataTable GetListItems(string listName, string queryWhere = "", string queryOrder = "")
        {
            ListData list = _Parent.Lists[listName];
            DataTable dt = GetListItems(list, queryWhere, queryOrder);
            return dt;
        }
        public DataTable GetListItems(int listId, string queryWhere = "", string queryOrder = "")
        {
            ListData list = _Parent.Lists.GetListById(listId);
            DataTable dt = GetListItems(list, queryWhere, queryOrder);
            return dt;
        }
        public DataTable GetListItems(ListData listData, string queryWhere = "", string queryOrder = "")
        {
            if (string.IsNullOrEmpty(queryWhere))
            {
                queryWhere = string.Format(SQLPatterns.WHERE_LIST_ID_PATTERN, AllDataColumns.COLUMN_LIST_ID, listData.Id);
            }
            else
            {
                queryWhere = string.Format(SQLPatterns.WHERE_WITH_LIST_ID_PATTERN, queryWhere, AllDataColumns.COLUMN_LIST_ID, listData.Id);
            }

            if (!string.IsNullOrEmpty(queryOrder))
            {
                queryOrder = string.Format(SQLPatterns.ORDER_BY_PATTERN, queryOrder);
            }

            List<string> columns = new List<string>();
            AllDataItemAttribute[] attributes = Propertyes.GetAllDataItemAttributes(typeof(AllDataItemBase));
            foreach (AllDataItemAttribute attr in attributes)
            {
                columns.Add(string.Format(SQLPatterns.COLUMN_PATTERN, attr.StaticName));
            }

            columns.AddRange(listData.GetColumnsData().Select(x => string.Format(SQLPatterns.COLUMN_PATTERN, x.AllDataColumnName)).ToArray());
            string querySelect = String.Join(",", columns);

            string query = string.Format(SQLPatterns.SELECT_PATTERN, querySelect, _Parent.Config.TableAllDataName, queryWhere, queryOrder);
            query = AllDataParser.CheckColumns(query, listData.GetColumnsData());
            DataTable dt = _Parent.DatabaseAccess.GetData(query);
            AllDataParser.CheckColumns(dt, listData.GetColumnsData());
            return dt;
        }
        public void ExucuteQuery(string query)
        {
            _Parent.DatabaseAccess.ExecuteQuery(query);
        }

        public AllDataItemsCollection<T> GetItems<T>(SQLinq<T> query)
        {
            ListData list = _Parent.Lists.CheckAndGetList<T>();

            SQLinqResult querySQLinq = query.ToSQL();
            string queryStr = AllDataParser.CheckAndGetSQLQuery(list, querySQLinq, _Parent.Config.TableAllDataName);

            AllDataItemsCollection<T> items = _Parent.DatabaseAccess.ReadData<T>(queryStr, querySQLinq.GetSQLParametrs(), list.GetColumnsData());
            return items;
        }

        internal static T GetNewListItemStatic<T>(int id)
        {
            Type itemType = typeof(T);
            ConstructorInfo constructor = itemType.GetConstructor(new Type[] { });

            if (constructor == null)
            {
                throw new Exception(string.Format("Class '{0}' not have default constructor.", itemType));
            }

            T item = (T)constructor.Invoke(new object[] { });
            PropertyInfo listIdProperty = itemType.GetProperty(AllDataColumns.COLUMN_LIST_ID);
            listIdProperty.SetValue(item, id);
            return item;
        }
        public T GetNewListItem<T>(int id)
        {
            T item = AllData.GetNewListItemStatic<T>(id);
            return item;
        }
        public long AddItem<T>(T newItem)
        {
            List<T> newItems = new List<T>()
            {
                newItem
            };

            List<long> newIds = AddItems<T>(newItems);
            if (newIds.Count > 0)
            {
                return newIds[0];
            }

            return -1;
        }
        public List<long> AddItems<T>(List<T> newItems)
        {
            ListData list = _Parent.Lists.GetList<T>();
            List<long> ids = new List<long>();
            string allQueryes = String.Empty;
            bool isMultiInsert = newItems.Count > 1;

            PropertyInfo[] basePropertyes = Propertyes.GetAllDataItemPropertyes(typeof(AllDataItemBase));
            Type columnAttribute = typeof(AllDataItemAttribute);
            
            foreach (T itemT in newItems)
            {
                AllDataItemBase item = itemT as AllDataItemBase;
                
                if (item.ListId == -1)
                {
                    item.ListId = list.Id;
                }

                //Set dates
                item.CreateDate = DateTime.Now;
                item.ModifyDate = DateTime.Now;

                List<string> columns = new List<string>();
                List<string> values = new List<string>();

                //Base columns
                foreach (PropertyInfo property in basePropertyes)
                {
                    AllDataItemAttribute attr = property.GetCustomAttribute(columnAttribute) as AllDataItemAttribute;

                    if (!AllDataColumns.COLUMN_NO_USE_IN_INSERT.Contains(attr.StaticName))
                    {
                        columns.Add(string.Format(SQLPatterns.COLUMN_PATTERN, attr.StaticName));
                        values.Add(SQLPatterns.GetValue(property.GetValue(item), attr.Type));
                    }
                }

                //Item columns
                PropertyInfo[] classPropertyes = Propertyes.GetAllDataItemPropertyesNoBase(item.GetType());

                foreach (PropertyInfo property in classPropertyes)
                {
                    AllDataItemAttribute attr = property.GetCustomAttribute(columnAttribute) as AllDataItemAttribute;

                    columns.Add(string.Format(SQLPatterns.COLUMN_PATTERN, attr.StaticName));
                    values.Add(SQLPatterns.GetValue(property.GetValue(item), attr.Type));
                }
                
                //Query
                string query = string.Format(SQLPatterns.ADD_ITEM_PATTERN, _Parent.Config.TableAllDataName, String.Join(",", columns), String.Join(",", values));
                query = AllDataParser.CheckColumns(query, list.GetColumnsData());

                if (isMultiInsert)
                {
                    allQueryes += query;
                }
                else
                {
                    query += SQLPatterns.SELECT_SCOPE_IDENTITY;

                    long id = _Parent.DatabaseAccess.ExecuteScalarLong(query);

                    if (id == -1)
                    {
                        throw new Exception("Not add item");
                    }

                    ids.Add(id);
                }
            }

            if (isMultiInsert)
            {
                _Parent.DatabaseAccess.GetData(allQueryes);
            }

            return ids;
        }
        public void UpdateItem(AllDataItemBase newItem)
        {
            List<AllDataItemBase> newItems = new List<AllDataItemBase>()
            {
                newItem
            };
            UpdateItems(newItems);
        }
        public void UpdateItems(List<AllDataItemBase> newItems)
        {
            PropertyInfo[] basePropertyes = Propertyes.GetAllDataItemPropertyes(typeof(AllDataItemBase));
            Type columnAttribute = typeof(AllDataItemAttribute);

            string allQueryes = String.Empty;

            foreach (AllDataItemBase item in newItems)
            {
                if (item.ListId == -1)
                {
                    throw new Exception("List Id not set");
                }

                //Set dates
                item.ModifyDate = DateTime.Now;

                List<string> setValues = new List<string>();

                //Base columns
                foreach (PropertyInfo property in basePropertyes)
                {
                    AllDataItemAttribute attr = property.GetCustomAttribute(columnAttribute) as AllDataItemAttribute;

                    if (!AllDataColumns.COLUMN_NO_USE_IN_UPDATE.Contains(attr.StaticName))
                    {
                        string value = SQLPatterns.GetValue(property.GetValue(item), attr.Type);
                        setValues.Add(string.Format(SQLPatterns.SET_VALUE_PATTERN, attr.StaticName, value));
                    }
                }

                //Item columns
                PropertyInfo[] classPropertyes = Propertyes.GetAllDataItemPropertyesNoBase(item.GetType());

                foreach (PropertyInfo property in classPropertyes)
                {
                    AllDataItemAttribute attr = property.GetCustomAttribute(columnAttribute) as AllDataItemAttribute;

                    string value = SQLPatterns.GetValue(property.GetValue(item), attr.Type);
                    setValues.Add(string.Format(SQLPatterns.SET_VALUE_PATTERN, attr.StaticName, value));
                }

                //Get List
                ListData list = _Parent.Lists.GetListById(item.ListId);

                //Query
                string query = string.Format(SQLPatterns.UPDATE_ITEM_PATTERN, _Parent.Config.TableAllDataName, String.Join(",", setValues), AllDataColumns.COLUMN_ID, item.Id);
                query = AllDataParser.CheckColumns(query, list.GetColumnsData());

                allQueryes += query;
            }

            _Parent.DatabaseAccess.ExecuteQuery(allQueryes);
        }
        public void DeleteItem(long id)
        {
            List<long> ids = new List<long>()
            {
                id
            };
            DeleteItems(ids);
        }
        public void DeleteItems(List<long> ids)
        {
            string query = String.Empty;

            foreach (long id in ids)
            {
                query += string.Format(SQLPatterns.DELETE_ITEM_PATTERN, _Parent.Config.TableAllDataName, AllDataColumns.COLUMN_ID, id);
            }

            _Parent.DatabaseAccess.ExecuteQuery(query);
        }

        
    }

    
}
